package com.pingan.haofang.searchcloud.common.handler;

import com.pingan.haofang.gov.sm.account.common.handler.AbstractExceptionHandler;
import com.pingan.haofang.searchcloud.common.exception.AccessForbbidenException;
import com.pingan.haofang.searchcloud.common.exception.FieldException;
import com.pingan.haofang.searchcloud.common.exception.FieldExceptionItem;
import com.pingan.haofang.searchcloud.common.exception.NotFoundException;
import com.pingan.haofang.searchcloud.common.exception.SystemException;
import com.pingan.haofang.searchcloud.common.exception.UnauthorizedException;
import com.pingan.haofang.searchcloud.common.vo.ExceptionVO;
import com.pingan.haofang.searchcloud.common.vo.FieldExceptionVO;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * WEB异常处理器
 *
 * @author LUYI374
 * @date 2017年2月16日
 * @since 1.0.0
 */
@ControllerAdvice
public class WebExceptionHandler extends AbstractExceptionHandler{

    static Logger LOG = LoggerFactory.getLogger(WebExceptionHandler.class);

    @Autowired
    private MessageSource messageSource;

    @ExceptionHandler(value = FieldException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    @ResponseBody
    public FieldExceptionVO handleFieldException(FieldException e) {
        FieldExceptionVO vo = new FieldExceptionVO();
        fillExceptionVO(e, vo);

        if (CollectionUtils.isNotEmpty(e.getFieldErrors())) {
            Map<String, String> fieldMap = new HashMap<String, String>(e.getFieldErrors().size());
            for (FieldExceptionItem item : e.getFieldErrors()) {
                String itemMessage = item.getMessage();
                try {
                    itemMessage = messageSource.getMessage(item.getMessage(), item.getArgs(),
                            LocaleContextHolder.getLocale());
                } catch (NoSuchMessageException ex) {
                    ; // ignore
                }
                fieldMap.put(item.getField(), itemMessage);
            }
            vo.setFieldErrors(fieldMap);
        }
        return vo;
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    @ResponseBody
    public FieldExceptionVO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        FieldExceptionVO vo = new FieldExceptionVO();
        if (e.getBindingResult() != null) {
            Map<String, String> fieldMap = new HashMap<>(e.getBindingResult().getFieldErrors().size());
            for (FieldError item : e.getBindingResult().getFieldErrors()) {
                String itemMessage = item.getDefaultMessage();
                try {
                    itemMessage = messageSource.getMessage(item.getDefaultMessage(), item.getArguments(),
                            LocaleContextHolder.getLocale());
                } catch (Exception ex) {
                    ; // ignore
                }
                fieldMap.put(item.getField(), itemMessage);
            }
            vo.setFieldErrors(fieldMap);
        }
        return vo;
    }

    /**
     * 未找到数据
     *
     * @param e
     * @return
     */
    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ResponseBody
    public ExceptionVO handleNotFoundException(NotFoundException e) {
        ExceptionVO vo = new ExceptionVO();
        fillExceptionVO(e, vo);
        return vo;
    }

    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    @ResponseBody
    public FieldExceptionVO handleBindException(BindException e) {
        FieldExceptionVO vo = new FieldExceptionVO();
        if (CollectionUtils.isNotEmpty(e.getFieldErrors())) {
            vo.setFieldErrors(e.getFieldErrors().stream()
                    .collect(Collectors.toMap(FieldError::getField,
                            error -> messageSource.getMessage("common.property.type.convert.error", new Object[]{},
                                    LocaleContextHolder.getLocale()))));
        }
        return vo;
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    @ResponseBody
    public FieldExceptionVO handleTypeMismatchException(MethodArgumentTypeMismatchException e) {
        FieldExceptionVO vo = new FieldExceptionVO();
        vo.setFieldErrors(new HashMap<String, String>() {{
            put(e.getName(),
                    messageSource.getMessage("common.property.type.convert.error", new Object[]{},
                            LocaleContextHolder.getLocale()));
        }});

        return vo;
    }

    @ExceptionHandler(AccessForbbidenException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ResponseBody
    public ExceptionVO handleAccessForbbidenException(AccessForbbidenException e) {
        ExceptionVO vo = new ExceptionVO();
        fillExceptionVO(e, vo);
        return vo;
    }

    @ExceptionHandler(UnauthorizedException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ResponseBody
    public ExceptionVO handleUnauthorizedException(UnauthorizedException e) {
        ExceptionVO vo = new ExceptionVO();
        fillExceptionVO(e, vo);
        return vo;
    }

    @ExceptionHandler(SystemException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    @ResponseBody
    public ExceptionVO handleSystemException(SystemException e) {
        LOG.error(e.getMessage(), e);
        ExceptionVO vo = new ExceptionVO();
        fillExceptionVO(e, vo);
        return vo;
    }


    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ExceptionVO globalError(Exception e) {
        LOG.error(e.getMessage(), e);
        ExceptionVO vo = new ExceptionVO();
        fillExceptionVO(e, vo);
        return vo;

    }

    @ResponseStatus(HttpStatus.CONFLICT)
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ExceptionVO throwValidationException(ConstraintViolationException e) {
        LOG.error(e.getMessage(), e);
        ExceptionVO vo = new ExceptionVO();
        fillExceptionVO(e, vo);
        return vo;

    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public ExceptionVO handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        LOG.error(e.getMessage(), e);
        ExceptionVO vo = new ExceptionVO();
        fillExceptionVO(e, vo);
        return vo;
    }

    /**
     * 填充异常响应消息
     *
     * @param e
     * @param vo
     */
    private void fillExceptionVO(Exception e, ExceptionVO vo) {
        if (e instanceof SystemException) {
            SystemException se = (SystemException) e;
            if (se.getMessage() != null) {
                String message = e.getMessage();
                try {
                    message = messageSource.getMessage(e.getMessage(), se.getArgs(), LocaleContextHolder.getLocale());
                } catch (NoSuchMessageException ex) {
                    ; // ignore
                }
                vo.setMessage(message);
            }
            vo.setErrorCode(se.getErrorCode() == null ? null : String.valueOf(se.getErrorCode()));
        } else {
            vo.setErrorCode("500");
            vo.setMessage(e.getMessage());

//            StringBuilder throwablesInfos = new StringBuilder(256);
//            ByteArrayOutputStream throwableOut = new ByteArrayOutputStream();
//            PrintStream throwableStream = new PrintStream(throwableOut);
//            e.printStackTrace(throwableStream);
//            throwablesInfos.append(new String(throwableOut.toByteArray()));
//            vo.setMessage(throwablesInfos.toString());
        }

    }
}
